//
//  EnumExample.swift
//  SwiftTest
//
//  Created by bym on 2019/8/10.
//  Copyright © 2019 ZZ. All rights reserved.
//

import UIKit

class EnumExample: NSObject {
	override init() {
		super.init()
		calculate()
	}
	
	//每个枚举定义了一个全新的类型。像 Swift 中其他类型一样
	enum CompassPoint {
		case north
		case south
		case east
		case west
	}
	//多个成员值可以出现在同一行上，用逗号隔开：
	//设置原始值 Plant.mercury 的显式原始值为 1，Planet.venus 的隐式原始值为 2，依次类推
	//当使用字符串作为枚举类型的原始值时，每个枚举成员的隐式原始值为该枚举成员的名称。
	enum Planet:Int {
		case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
	}
	
	//MARK:-- 枚举成员的遍历
	//令枚举遵循 CaseIterable 协议。Swift 会生成一个 allCases 属性，用于表示一个包含枚举所有成员的集合。下面是一个例子：
	enum Beverage: CaseIterable {
		case coffee, tea, juice
	}
	
	// 遍历
	func itera() {
		for beverage in Beverage.allCases {
			ZZLog(beverage)
		}
		
		//Array不支持 tuple不支持 dic不支持 set不支持
		let arr:[Any] = ["a",1,3.0,false]
		let tuple = (1,2,3)
		let dic = [1:"one",2:"two",3:"three"]
		let set = [1,2,3]
	}
	
	// MARK:--关联值
	
	//你可以定义 Swift 枚举来存储任意类型的关联值
	//把其他类型的值和成员值一起存储起来
	enum Barcode {
		case upc(Int, Int, Int, Int)
		case qrCode(String)
	}
	func relationValueExample() {
		//这时，原始的 Barcode.upc 和其整数关联值被新的 Barcode.qrCode 和其字符串关联值所替代
		//Barcode 类型的常量和变量可以存储一个 .upc 或者一个 .qrCode（连同它们的关联值），但是在同一时间只能存储这两个值中的一个。
		var productBarcode = Barcode.upc(8, 85909, 51226, 3)
		productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
		
		//关联值可以被提取出来作为 switch 语句的一部分。
		//你可以在 switch 的 case 分支代码中提取每个关联值作为一个常量（用 let 前缀）或者作为一个变量（用 var 前缀）来使用：
		
		switch productBarcode {
		case .upc(let numberSystem, let manufacturer, let product, let check):
			print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
		case .qrCode(let productCode):
			print("QR code: \(productCode).")
		}
		
		switch productBarcode {
		case let .upc(numberSystem, manufacturer, product, check):
			print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
		case let .qrCode(productCode):
			print("QR code: \(productCode).")
		}
	}
	

	// MARK:--原始值
	
	//枚举类型 ASCIIControlCharacter 的原始值类型被定义为 Character，并设置了一些比较常见的 ASCII 控制字符。
	//原始值可以是字符串、字符，或者任意整型值或浮点型值。每个原始值在枚举声明中必须是唯一的。
	enum ASCIIControlCharacter: Character {
		case tab = "\t"
		case lineFeed = "\n"
		case carriageReturn = "\r"
	}
	
	
	// MARK:--原始值和关联值比较
	
	//原始值和关联值是不同的。
	//原始值是在定义枚举时被预先填充的值，像上述三个ASCII码。对于一个特定的枚举成员，它的原始值始终不变
	//关联值是创建一个基于枚举成员的常量或变量时才设置的值，枚举成员的关联值可以变化。
	
	func originalValue() {
		//原始值的隐式赋值
			//在使用原始值为整数或者字符串类型的枚举时，不需要显式地为每一个枚举成员设置原始值，Swift 将会自动为你赋值。
			//当使用整数作为原始值时，隐式赋值的值依次递增1。如果第一个枚举成员没有设置原始
			//值，其原始值将为 0。
			//具体查看Planet定义
		
		//使用枚举成员的 rawValue 属性可以访问该枚举成员的原始值：
		let earthsOrder = Planet.earth.rawValue
		// earthsOrder 值为 3
		
		//如果在定义枚举类型的时候使用了原始值，那么将会自动获得一个初始化方法，
		//这个方法接收一个叫做 rawValue 的参数，参数类型即为原始值类型，返回值则是枚举成员或 nil
		let positionToFind = 11
		if let possiblePlanet = Planet(rawValue: positionToFind){
			switch possiblePlanet {
			case .earth:
				print("Mostly harmless")
			default:
				print("Not a safe place for humans")
			}
		} else {
			print("There isn't a planet at position \(positionToFind)")
		}

	}
	
	// MARK:--递归枚举
	
	//递归枚举	它有一个或多个枚举成员使用该枚举类型的实例作为关联值
	//你可以在枚举成员前加上 indirect 来表示该成员可递归。
	
	enum ArithmeticExpression {
		case number(Int)
		//枚举成员 addition 和 multiplication 的关联值也是算术表达式
		indirect case addition(ArithmeticExpression, ArithmeticExpression)
		indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
	}
	
	/* 或者简写成:
	indirect enum ArithmeticExpression {
	case number(Int)
	case addition(ArithmeticExpression, ArithmeticExpression)
	case multiplication(ArithmeticExpression, ArithmeticExpression)
	}
	*/
	func evaluate(_ expression: ArithmeticExpression) -> Int {
		switch expression {
		case let .number(value):
			return value
		case let .addition(left, right):
			return evaluate(left) + evaluate(right)
		case let .multiplication(left, right):
			return evaluate(left) * evaluate(right)
		}
	}
	
	func calculate() {
		let five = ArithmeticExpression.number(5)
		let four = ArithmeticExpression.number(4)
		let sum = ArithmeticExpression.addition(five, four)
		let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
		ZZLog(evaluate(product))
	}

}
